const utils = require("../utils/utils");
const BuildMoudle = require("../module/BuildModule");
const fs = require("fs");
const path = require("path");
const walk = require("../utils/walk");
const Define = require("../define");

const rgxPropid = getRgxParam("propid");
const rgxTcnt = getRgxParam("tcnt");
const rgxDismisschinacc = getRgxParam("dismisschinacc");
const rgxT = getRgxParam("t");
const rgxDedution = getRgxParam("deductionticket");
const rexXyid = getRgxParam("xyid_pb");
const rgxN = /\s/g;

function getRgxParam(key) {
    return new RegExp("{{" + key + "}}", "g");
}

const dismissMah = "'{{0,1,1,1},{0,2,2,1},{0,3,2,1},{0,4,3,1}}'";
const dismissCard = "'{{0,1,1,1},{0,2,2,1},{0,3,3,1},{0,4,4,1}}'";
const dismissCard5 = "'{{0,1,1,1},{0,2,2,1},{0,3,3,1},{0,4,4,1},{0,5,5,1}}'";

/**回放协议 */
const xyidPbMah = "'20112:2|20102:3|20106:3|20113:3|20107:3|20018:3|20105:3';";
const xyidPbCard = "'20112:2|20503:3';";

const prop = {
    propID_fangka: 101032,
    propID_zuanshi: 101034,
    propID_xiaofangka: 101033,
    smallRoomCardRatio: 100,
};

const cardDirs = ["mengqi_landlord", "mengqi_paodekuai"];

/**过滤器 */
class Filter {
    static _noAA = ["mengqi_fillpit"];
    static noAA(dirName) {
        for (let aa of this._noAA) {
            if (dirName.indexOf(aa) > -1) {
                return true;
            }
        }
        return false;
    }
}

/**
 *
 * 包厢：模式1【开始扣道具局数,最多局数,道具ID,道具数量,
 *            是否续桌,支付类型,透支道具ID,最多透支数(默认10)】
 * t4='1,99,100970,200,0,1,100970,0,0';
 *
 * 茶馆：模式1【gameid，游戏玩家人数，
 *            最小局数，最大局数，消耗房卡数量，
 *            包房模式（0 定局，1定分，2定时），
 *            初始财富（默认10000），支付类型（0是馆主支付，1是AA支付），消耗道具ID】
 * t4='20197,2,1,1002,200,0,0,21,100970';
 */

class GpBuilder {
    /**
     * @type {BuildMoudle}
     */
    moudle;

    /**
     * 初始化信息
     * AreaConfig.json
     */
    info;

    /**
     * info文件当前路径
     */
    dir;

    /**
     * 模版
     * @type {string}
     */
    temp;

    /**
     * 换行
     */
    lineFeed = false;

    /**
     * 地区游戏列表
     * GameSubConfig.json
     */
    subgameMap;

    constructor(lineFeed) {
        this.lineFeed = lineFeed;
    }

    build(dir, info, mod) {
        this.moudle = mod;
        this.info = utils.copyJson(info);
        this.info.propID_fangka = prop.propID_fangka;
        this.info.propID_xiaofangka = prop.propID_xiaofangka;
        this.info.propID_zuanshi = prop.propID_zuanshi;
        this.dir = dir;
        this.initTemp();
        this.walkSubGame();
    }

    initTemp() {
        this.temp = utils.readFile(this.moudle.tempPath);
        this.temp = this.temp.replace(rgxPropid, this.info.propID_fangka);
        this.temp = this.temp.replace(rgxDedution, `'101034,101186,101187,150188'`);
    }

    walkSubGame() {
        const jsonPath = path.join(this.dir, Define.FILE_SUBGAME);
        this.subgameMap = utils.readObj(jsonPath);

        let game;
        let subgameKeys = Object.keys(this.subgameMap);
        for (let id of subgameKeys) {
            game = this.subgameMap[id];
            this.buildSubGame(game);
        }
    }

    buildSubGame(game) {
        // '/Users/wuhaotian/work/cocos/chinacollection3d/assets/resources/area/mengqi_hebei';
        // /Users/wuhaotian/work/cocos/chinacollection3d/assets/bundles/subgame
        // utils.logTips(game);
        const subgamePath = path.join(this.moudle.infoPath, "../../bundles/subgame/", game.BundleName);

        let subgameInfo = walk.dirWalk(subgamePath, Define.FILE_GAME_INFO);
        subgameInfo.GameName = subgameInfo.GameName.replace(/[\r\n]/g, "");
        let rule = walk.dirWalk(subgamePath, Define.FILE_RULE);
        let ruleTea = walk.dirWalk(subgamePath, Define.FILE_RULE_TEA);
        if (!rule || !ruleTea) return;
        this.parseSubGameRule(game, subgameInfo, rule, false);
        this.parseSubGameRule(game, subgameInfo, ruleTea, true);

        if (ruleTea.VirtualGameRuleID > 0) {
            this.parseSubGameRule(game, subgameInfo, ruleTea, true, ruleTea.VirtualGameRuleID);
        }
    }

    getIsMah(name) {
        return cardDirs.indexOf(name) < 0;
    }

    getTitle(isTea) {
        return isTea ? "茶馆" : "包厢";
    }

    createChunk(name, content) {
        return `\n${name}\n${content}`;
    }

    /**
     * 解析GameRule.json配置
     * @param {boolean} isTea
     * @param {boolean} mulitpleId
     */
    parseSubGameRule(game, subgameInfo, rule, isTea, mulitpleId) {
        let _rule = utils.copyJson(rule);
        let ren = this.findByKey("playerCount", _rule);
        let renCtrls = ren[0].ctrls;
        let temp = this.temp;
        const isCard = subgameInfo.GameType != "MaJiang";

        if (isTea) {
            temp += "noplayerdeletetable=1;";
        }

        temp = temp.replace(rexXyid, isCard ? xyidPbCard : xyidPbMah);

        let renList = [];
        let levels = [];
        //人数整理成列表
        for (let index in renCtrls) {
            let ctrlRow = renCtrls[index];
            for (let ctrl of ctrlRow) {
                renList.push(ctrl);
                levels.push(ctrl.linkageLevel);
            }
        }

        const len = renList.length;
        let chunkName = "";
        let roundMap = {};
        let ctrl;
        // 局数选项最大长度
        let tcnt = 0;
        for (let i = 0; i < len; i++) {
            ctrl = renList[i];
            //标题
            chunkName = `### ${this.getTitle(isTea)} ${subgameInfo.GameName} ${ctrl.text} ${
                mulitpleId ? "多区配置" + mulitpleId : ""
            }\n`;
            let roundList = this.findRoundByLinkageLevel(ctrl.linkageLevel, rule);
            if (!roundList) {
                utils.logError(game.BundleName, "局数配置不存在");
                return;
            }
            if (roundList.length > tcnt) {
                tcnt = roundList.length;
            }
            roundMap[ctrl.linkageLevel] = {
                player: ctrl,
                round: roundList,
                name: chunkName,
                len: roundList.length,
            };
        }
        this.parseRoundMap(game, subgameInfo, roundMap, temp, isTea, tcnt, mulitpleId);
    }

    /**
     *
     * @param {*} roundMap
     * @param {*} temp
     */
    parseRoundMap(game, subgameInfo, roundMap, temp, isTea, tcnt, mulitpleId) {
        const gameid = game.GameID;
        const isCard = subgameInfo.GameType != "MaJiang";
        const noAA = Filter.noAA(game.BundleName);
        let levelsKeys = Object.keys(roundMap);
        let round;
        let player;
        let level;
        let chunkName;
        //快速开局
        let quick = {};

        for (let i = 0; i < levelsKeys.length; i++) {
            level = levelsKeys[i];
            const roundBody = roundMap[level];
            round = roundBody.round;
            player = roundBody.player;
            const playerCount = utils.extractNum(player.text);
            chunkName = roundMap[level].name;
            //t初始化
            let tn = [];
            let isNot2ren = i > 0;
            let roundCache = {};
            let content = temp;
            for (let t = 1; t <= tcnt; t++) {
                let tData = this.getTData(roundMap, level, t);
                if (isTea) {
                    //茶馆
                    //开始生成t1......
                    for (let j = i; j < levelsKeys.length; j++) {
                        let ttData = this.getTData(roundMap, levelsKeys[j], t);
                        let cacheKey =
                            j > i ? this.circleToRound(ttData.roundData.round) : ttData.roundData.round;
                        if (!tn[j]) tn[j] = [];
                        if (roundCache[cacheKey] || (isCard && j > i)) continue;
                        roundCache[cacheKey] = 1;
                        if (ttData.roundData.round == 99) continue;
                        tn[j].push(this.createTTeaByTData(ttData, gameid, playerCount, j > i, mulitpleId));
                    }
                } else {
                    //包厢
                    for (let k = 0; k <= 1; k++) {
                        if (noAA) {
                            if (k > 0) continue;
                        }
                        if (!tn[k]) tn[k] = [];
                        if (tData.roundData.round == 99) continue;
                        tn[k].push(this.createTByTData(tData, k > 0));
                    }
                    //生成快速开局
                    if (isNot2ren || playerCount == 5) {
                        if (tData.roundData.round == 99) continue;
                        this.createQuickData(tData, chunkName, quick, playerCount);
                    }
                }
            }
            if (tn.length) {
                this.writeT(game, subgameInfo, tn, i, chunkName, content, isNot2ren, isTea, playerCount);
            }
        }
        this.writeQuick(game, subgameInfo, quick, noAA, game);
    }

    createQuickData(tData, chunkName, quick, playerCount) {
        const roundData = tData.roundData;
        const count = tData.count;
        const cost = tData.cost;
        const len = playerCount - 2;

        for (let diff = 1; diff <= len; diff++) {
            let _count = count - diff;
            if (_count >= 2) {
                if (roundData.round_quick == 99) {
                    continue;
                }
                let countKey = `${count}->${_count}`;
                if (!quick[countKey])
                    quick[countKey] = {
                        name: `### 快速开局 ${countKey}人提前 ${chunkName}`,
                    };
                quick[countKey][roundData.round_quick] = {
                    all: this.createTQuick(roundData.round_quick, this.info.propID_xiaofangka, cost, 0),
                    aa: this.createTQuick(
                        roundData.round_quick,
                        this.info.propID_xiaofangka,
                        Math.max(100, Math.ceil(cost / 100 / _count) * 100),
                        1
                    ),
                };
            }
        }
    }

    /**
     * tn写入文件
     * @param {any} subgameInfo
     * @param {number} i
     * @param {string} chunkName
     * @param {string} content
     * @param {boolean} isNot2ren
     */
    writeT(game, subgameInfo, tn, i, chunkName, content, isNot2ren, isTea, playerCount) {
        const isCard = subgameInfo.GameType != "MaJiang";
        let merge = this.listMerge(tn);
        content = content.replace(rgxT, this.listMapToTn(merge));
        content = content.replace(rgxTcnt, `${merge.length}`);
        content = content.replace(
            rgxDismisschinacc,
            isCard ? (playerCount == 5 ? dismissCard5 : dismissCard) : dismissMah
        );
        if (isNot2ren) {
            if (!isTea && !isCard) {
                content += "otherappids='';";
            }
        }
        this.write(game, subgameInfo, chunkName + this.removeN(content), i == 0 && !isTea);
    }

    /**
     * 写入快速开局
     * @param {*} quick
     */
    writeQuick(game, subgameInfo, quick, noAA) {
        //快速开局写入
        for (let renCount in quick) {
            let quickChunk = quick[renCount];
            const name = quickChunk.name;
            delete quickChunk["name"];
            let quickKeys = Object.keys(quickChunk);
            let qLen = quickKeys.length;
            let tn = [];
            for (let q = 0; q < quickKeys.length; q++) {
                let key = quickKeys[q];
                let item = quickChunk[key];

                for (let i = 0; i < 2; i++) {
                    if (!tn[i]) tn[i] = [];
                    if (i == 0) {
                        tn[i].push(`${item.all}`);
                    } else {
                        if (!noAA) {
                            tn[i].push(`${item.aa}`);
                        }
                    }
                }
            }
            let merge = this.listMerge(tn);
            let content = `tcnt=${merge.length};\n` + this.listMapToTn(merge);
            this.write(game, subgameInfo, name + this.removeN(content));
        }
    }

    /**
     * 列表合并
     * @param {*} list
     * @returns
     */
    listMerge(list) {
        let _list = utils.copyJson(list);
        let r = [];
        for (let item of list) {
            if (item != undefined) {
                r = r.concat(item);
            }
        }
        return r;
    }

    /**
     *
     * @param {string[]} list
     * @returns {string}
     */
    listMapToTn(list) {
        let t = "";
        list.forEach((str, index) => {
            t += `t${index + 1}=${str}`;
        });
        return t;
    }

    /**
     * tn数据解析
     * @param {*} roundMap
     * @param {*} level
     * @param {*} t
     * @returns
     */
    getTData(roundMap, level, t) {
        if (!roundMap[level]) return null;
        let round = roundMap[level].round;
        let player = roundMap[level].player;
        let curRound = round[t - 1];
        let roundData = this.getRoundData(curRound);
        let count = utils.extractNum(player.text);
        let cost = !curRound ? 200 : curRound[`allCost${count}`] * 100;
        let costAA = !curRound ? 200 : curRound[`aaCost${count}`] * 100;

        return {
            round: round,
            curRound: curRound,
            count: count,
            roundData: roundData,
            cost: cost,
            costAA: costAA,
        };
    }

    createTByTData(tData, aa = false) {
        return this.createT(
            tData.roundData.round,
            this.info.propID_xiaofangka,
            aa ? tData.costAA : tData.cost,
            aa ? 1 : 0
        );
    }

    createT(round, propid, cost, pay) {
        return `'1,${round},${propid},${cost},0,${pay},${propid},0,0';\n`;
    }

    circleToRound(round) {
        return round > 1000 ? (round - 1000) * 4 : round;
    }

    createTTeaByTData(tData, gameid, count, noCircle, mulitpleId) {
        const round = tData.roundData.round;
        return this.createTTea(
            gameid,
            count ?? tData.count,
            noCircle ? (round > 1000 ? (round - 1000) * 4 : round) : round,
            tData.cost,
            tData.roundData.baofangmoshi,
            this.info.propID_xiaofangka,
            mulitpleId
        );
    }

    createTTea(gameid, ren, maxRound, cost, baofangmoshi, propid, mulitpleId) {
        if (maxRound < 1000 && baofangmoshi > 0) {
            baofangmoshi = 0;
        }
        if (mulitpleId > 0) {
            return `'${gameid},${ren},1,${maxRound},${cost},${baofangmoshi},0,21,${propid},${mulitpleId}';\n`;
        } else {
            return `'${gameid},${ren},1,${maxRound},${cost},${baofangmoshi},0,21,${propid}';\n`;
        }
    }

    createTQuickByTData(tData, payType, aa = false) {
        return this.createTQuick(
            tData.roundData.round_quick,
            this.info.propID_xiaofangka,
            aa ? tData.costAA : tData.cost,
            payType
        );
    }

    createTQuick(round, propid, cost, pay) {
        return `'1,${round},${propid},${cost},0,${pay},${propid},0,0';\n`;
    }

    /**
     * 清理换行
     * @param {string} str
     * @returns
     */
    removeN(str) {
        if (this.lineFeed) {
            return str;
        } else {
            return "\n" + str.replace(rgxN, "") + "\n\n ";
        }
    }

    /**
     *
     * @param {*} ctrl
     */
    getRoundData(ctrl) {
        let type = 1;
        let round = 99;
        let result = {
            round_type: type,
            round: round,
            round_quick: round,
            baofangmoshi: 0,
        };
        if (!ctrl) {
            return result;
        }

        let list = ctrl.nodeName.split(";");
        type = utils.extractNum(list[0].split("=")[1]);
        round = utils.extractNum(list[1].split("=")[1]);

        const type0 = type == 0;

        result.round_type = type;
        result.round = type0 ? 1000 + round : round;
        result.round_quick = type0 ? round * 4 : round;
        result.baofangmoshi = type0 ? 3 : 0;
        return result;
    }

    /**
     * 寻找局数
     * @param {number} linkageLevel
     * @param {*} rule
     * @returns {any[]}
     */
    findRoundByLinkageLevel(linkageLevel, rule) {
        let round = this.findByKey("playerRule", rule);
        for (let item of round) {
            if (item.show == undefined) {
                return this.ctrlsToList(item.ctrls);
            } else if (item.show.indexOf(linkageLevel) > -1) {
                return this.ctrlsToList(item.ctrls);
            }
        }
        return null;
    }

    /**
     * ctrls数据转为列表
     * @param {*} ctrls
     * @returns
     */
    ctrlsToList(ctrls) {
        let _ctrls = utils.copyJson(ctrls);
        let list = [];
        for (let index in _ctrls) {
            let ctrlList = _ctrls[index];
            for (let item of ctrlList) {
                list.push(item);
            }
        }
        return list;
    }

    /**
     * @param {string} key
     * @returns {any[]}
     */
    findByKey(key, rule) {
        const body = rule.categories[0];
        const result = [];
        let item;
        for (let ruleKey in body) {
            item = body[ruleKey];
            const name = item.nodeName;
            if (ruleKey.indexOf(key) > -1 || (name && name.indexOf(key) > -1)) {
                result.push(utils.copyJson(item));
            }
        }
        return result;
    }

    write(game, subgameInfo, content, frist) {
        const dirName = path.basename(this.dir);
        const name = subgameInfo.GameName + "_" + game.BundleName + ".md";
        if (!subgameInfo.GameName || !game.BundleName) {
            console.error("文件名错误", subgameInfo, "\n", game);
        }
        const outputPath = path.join(this.moudle.outputPath, dirName, name);
        const outputDir = path.dirname(outputPath);
        utils.logTips(content);
        try {
            let stat = fs.statSync(outputDir);
            utils.writeFile(outputPath, content + "\n\n\n", !frist);
        } catch (nouse) {
            fs.mkdirSync(outputDir);
            this.write(subgameInfo, content, frist);
        }
    }
}

module.exports = GpBuilder;
